#!/bin/sh
# Copyright (c) 2004-2017 Apple Inc.
#
# get-mobility-info
#
# Collect system & network configuration information.
#

PATH=/bin:/usr/bin:/sbin:/usr/sbin

OUTDIR=""
NO_PCAP=0
NO_TAR=0

while getopts f:PT OPTION ; do
	case ${OPTION} in
		f)
		   OUTDIR="${OPTARG}"
		   if [ ! -d "${OUTDIR}" ]; then
			echo "# ${PROGNAME}: \"${OUTDIR}\" is not a directory"
			exit 1
		   fi
		   ;;
		P)
		   NO_PCAP=1
		   ;;
		T)
		   NO_TAR=1
		   ;;
		\?)
		   ;;
	esac
done

#
# Disclaimer
#
cat <<_END_OF_DISCLAIMER

This diagnostic tool generates files that allow Apple to investigate issues
with your computer and help Apple to improve its products. The generated files
may contain some of your personal information, which may include, but not be
limited to, the serial number or similar unique number for your device, your
user name, or your computer name. The information is used by Apple in
accordance with its privacy policy (www.apple.com/privacy) and is not shared
with any third party. By enabling this diagnostic tool and sending a copy of
the generated files to Apple, you are consenting to Apple's use of the content
of such files.

_END_OF_DISCLAIMER

/bin/echo "Press 'Enter' to continue."
read reply

#
# Setup
#
PRIV=""
if [ ${EUID} -ne 0 ]; then
	PRIV="sudo"
fi

if [ -x /usr/bin/tail ]; then
	TAIL_2000="/usr/bin/tail -n 2000"
	TAIL_25000="/usr/bin/tail -n 25000"
else
	TAIL_2000="/bin/cat"
	TAIL_25000="/bin/cat"
fi

OUT="mobility-info-`date +'%Y.%m.%d.%H%M%S'`"

if [ -z $OUTDIR ]; then
	OUTDIR="/var/tmp"
	if [ -d ~/Desktop ]; then
		OUTDIR=~/Desktop
	elif [ "`readlink /tmp`" = "private/var/tmp" ]; then
		OUTDIR=/Library/Logs/DiagnosticReports
		if [ ! -d /Library/Logs/DiagnosticReports -a -d /Library/Logs/CrashReporter ]; then
			OUTDIR=/Library/Logs/CrashReporter
		fi
		mkdir -p ${OUTDIR}
	fi
fi

umask 077

WORKDIR=`mktemp -d -q "/tmp/${OUT}"`
if [ $? -ne 0 ]; then
	echo "Could not create snapshot directory"
	exit 1
fi

if [ $NO_TAR -eq 0 ]; then
	GZ_EXT=""
	GZ_OPT=""
	if [ -x /usr/bin/gzip ]; then
		GZ_EXT=".gz"
		GZ_OPT="-z"
	fi

	ARCHIVE=`mktemp -q "${OUTDIR}/${OUT}.tar${GZ_EXT}"`
	if [ $? -ne 0 ]; then
		echo "Could not create snapshot archive"
		rm -rf "${WORKDIR}"
		exit 1
	fi
fi

cd "${WORKDIR}"

echo ""
echo "Please wait, collecting information and statistics"
echo ""

#
# collect packet capture with kernel ring buffer if available
#
stop_pcap () {
	#
	# Stop the packet capture
	#
	if [ ${PCAP_STARTED} -ne 0 ]; then
		trap '' SIGINT
		/usr/local/bin/netdiagnose stop packetcapture			2>&1
		PCAP_STARTED=0
	fi
}

PCAP_STARTED=0
if [ -x /usr/local/bin/netdiagnose -a ${NO_PCAP} -ne 1 ]; then
	trap stop_pcap SIGINT
	/usr/local/bin/netdiagnose -p "${WORKDIR}" start packetcapture		2>&1
	PCAP_STARTED=1
fi

#
# get-network-info
#
if [ -x /System/Library/Frameworks/SystemConfiguration.framework/Resources/get-network-info ]; then
	/bin/sh /System/Library/Frameworks/SystemConfiguration.framework/Resources/get-network-info -s -c -P "${WORKDIR}"
elif [ -x /System/Library/Frameworks/SystemConfiguration.framework/get-network-info ]; then
	/bin/sh /System/Library/Frameworks/SystemConfiguration.framework/get-network-info -s -c -P "${WORKDIR}"
elif [ -x /System/Library/PrivateFrameworks/SystemConfiguration.framework/get-network-info ]; then
	/bin/sh /System/Library/PrivateFrameworks/SystemConfiguration.framework/get-network-info -s -c -P "${WORKDIR}"
fi

#
# processes
#
if [ -x /bin/ps ]; then
	/bin/ps axlww					> ps			2>&1
fi

#
# AirPort info
#
if [ -x /System/Library/PrivateFrameworks/Apple80211.framework/Resources/airport ]; then
	/System/Library/PrivateFrameworks/Apple80211.framework/Resources/airport --getinfo	\
							> airport		2>&1
fi

#
# collect wifi dump
#
if [ -x /usr/bin/wdutil -a -x /bin/ls ]; then
	${PRIV} /usr/bin/wdutil dump
	mkdir -p "wifi_dump"
	/bin/ls -1 /private/tmp/wifi-*						2>/dev/null	\
	| while read log
	do
		if [ -f "${log}" ]; then
			b="`basename ${log}`"
			${PRIV} cat "${log}"		> "wifi_dump/${b}"	2>&1
		fi
	done
fi

#
# OS info
#
if [ -e /System/Library/CoreServices/SystemVersion.plist ]; then
	cat /System/Library/CoreServices/SystemVersion.plist	\
							> SystemVersion.plist	2>&1
fi

#
# IOKit info
#
if [ -x /usr/sbin/ioreg ]; then
	/usr/sbin/ioreg -i -l -w 0			>  ioreg		2>&1
	/usr/sbin/ioreg -i -l -p IODeviceTree -w 0	>> ioreg		2>&1
fi

#
# Power Management info
#
if [ -x /usr/bin/pmset ]; then
	echo "#"							>  pmset
	echo "# pmset -g everything"					>> pmset
	echo "#"							>> pmset
	/usr/bin/pmset -g everything 2>/dev/null  | ${TAIL_25000}	>> pmset
fi

#
# Host configuration
#
if [ -x /usr/bin/hostinfo ]; then
	/usr/bin/hostinfo				> hostinfo		2>&1
fi
if [ -e /etc/hostconfig ]; then
	cat /etc/hostconfig				> etc.hostconfig	2>&1
fi

#
# System / network preferences
#
for f in										\
	/Library/Preferences/SystemConfiguration/com.apple.PowerManagement.plist	\
	/Library/Preferences/SystemConfiguration/com.apple.airport.preferences.plist	\
	/Library/Preferences/SystemConfiguration/com.apple.wifi.plist			\
	/Library/Preferences/com.apple.alf.plist					\
	/Library/Preferences/com.apple.sharing.firewall.plist				\
	/Library/Preferences/com.apple.wwand.plist					\

do
	if [ -e "${f}" ]; then
		b="`basename ${f}`"
		cat "${f}"				> "${b}"		2>&1
	fi
done

#
# Install log
#
if [ -e /var/log/install.log ]; then
	cat /var/log/install.log			> install.log		2>&1
fi

#
# System / network preferences (from other volumes)
#
mount -t hfs | grep "/Volumes/" | sed -e 's:^.* on /Volumes/::' -e 's: ([^(]*$::'	\
| while read volume
do
	V_PATH="/Volumes/${volume}"
	if [ -h "${V_PATH}" ]; then
		# if the path is a symlink
		continue
	fi

	for f in									\
		/Library/Preferences/SystemConfiguration/Networkinterfaces.plist	\
		/Library/Preferences/SystemConfiguration/preferences.plist		\

	do
		if [ -f "${V_PATH}/${f}" ]; then
			mkdir -p "OtherPreferences/${volume}"
			b="`basename ${f}`"
			cat "${V_PATH}/${f}"		> "OtherPreferences/${volume}/${b}"	2>&1
		fi
	done
done

#
# InternetSharing
#
if   [ -e /etc/bootpd.plist ]; then
	cat /etc/bootpd.plist							> bootpd.plist			2>&1
	cat /etc/com.apple.named.proxy.conf					> com.apple.named.proxy.conf	2>/dev/null
elif [ -e /Library/Preferences/SystemConfiguration/bootpd.plist ]; then
	cat /Library/Preferences/SystemConfiguration/bootpd.plist		> bootpd.plist			2>&1
	cat /Library/Preferences/SystemConfiguration/com.apple.named.proxy.conf	> com.apple.named.proxy.conf	2>/dev/null
fi

#
# mounted filesystems
#
mount							> mounted-filesystems	2>&1

${PRIV} cat /etc/hosts 					> etc.hosts		2>/dev/null

#
# kernel extensions statistic
#
if   [ -x /usr/sbin/kextstat ]; then
	/usr/sbin/kextstat				> kextstat		2>&1
fi

if [ -x /sbin/pfctl ]; then
	echo "#"					>  pf
	echo "# pfctl -s all"				>> pf
	echo "#"					>> pf
	${PRIV} /sbin/pfctl -s all			>> pf			2>&1
	echo "=============================="		>> pf
	echo "#"					>> pf
	echo "# pfctl -s References"			>> pf
	echo "#"					>> pf
	${PRIV} /sbin/pfctl -s References		>> pf			2>&1
	for ANCHOR in `${PRIV} pfctl -s Anchors -v 2>/dev/null`
	do
		echo "=============================="	>> pf
		echo "#"				>> pf
		echo "# pfctl -a ${ANCHOR} -s all"	>> pf
		echo "#"				>> pf
		${PRIV} /sbin/pfctl -a ${ANCHOR} -s all	>> pf			2>&1
	done
fi

#
# mach port info
#
if [ -x /usr/local/bin/lsmp ]; then
	${PRIV} /usr/local/bin/lsmp -a -v		> lsmp			2>&1
fi

#
# open files
#
if [ -x /usr/sbin/lsof ]; then
	${PRIV} /usr/sbin/lsof +c 0 -n -O -P -T q	> lsof			2>&1	&
	LSOF_PID=$!
	# Init a watchdog for lsof
	(
		WAIT_TIME=5
		while [ $WAIT_TIME -gt 0 ]
		do
			${PRIV} kill -0 ${LSOF_PID}	2>/dev/null
			if [ $? -eq 0 ]; then
				sleep 1
				# lsof is gathering data..
				WAIT_TIME=$((WAIT_TIME-1))
				continue
			fi

			# lsof completed gathering data
			break
		done

		if [ $WAIT_TIME -eq 0 ]; then
			# lsof timed out
			${PRIV} kill ${LSOF_PID}	2>/dev/null
		fi
	) &
fi

#
# [lib]dispatch info
#
if [ -x /usr/local/bin/ddt ]; then
    /bin/echo -n ""					>  dispatch-info
    for BIN in		\
	configd		\
	discoveryd	\

    do
	echo "#"					>> dispatch-info
	echo "# ddt -vkp ${BIN}"			>> dispatch-info
	echo "#"					>> dispatch-info
	${PRIV} /usr/local/bin/ddt -vkp ${BIN}		>> dispatch-info	2>&1
    done
fi

#
# OpenDirectory info
#
if [ -x /usr/bin/odutil ]; then
	echo "#"					>  od-info
	echo "# odutil show all"			>> od-info
	echo "#"					>> od-info
	${PRIV} /usr/bin/odutil show all		>> od-info		2>&1
fi

#
# Kerberos configuration
#
if [ -x /usr/bin/klist ]; then
	echo "#"					>  kerberos
	echo "# klist --verbose --all-content"		>> kerberos
	echo "#"					>> kerberos
	klist --verbose --all-content			>> kerberos	2>&1

	echo "#"					>> kerberos
	echo "# ktutil list"				>> kerberos
	echo "#"					>> kerberos
	${PRIV} /usr/sbin/ktutil --verbose list		>> kerberos	2>&1

	echo "#"					>> kerberos
	echo "# gsstool list --verbose"			>> kerberos
	echo "#"					>> kerberos
	/System/Library/PrivateFrameworks/Heimdal.framework/Helpers/gsstool list --verbose >> kerberos	2>&1
fi

#
# system profiler
#
if [ -x /usr/sbin/system_profiler ]; then
	system_profiler -xml 	SPEthernetDataType 	\
				SPFibreChannelDataType	\
				SPFireWireDataType 	\
				SPFirewallDataType 	\
				SPModemDataType		\
				SPNetworkDataType 	\
				SPThunderboltDataType 	\
				SPWWANDataType 		\
				SPAirPortDataType 	> system_profiler.spx	2>/dev/null
fi

#
# system usage statistics
#
/bin/echo -n ""						> system-statistics

if [ -x /usr/bin/uptime ]; then
	echo "#"					>> system-statistics
	echo "# uptime"					>> system-statistics
	echo "#"					>> system-statistics
	/usr/bin/uptime					>> system-statistics	2>&1
fi

if [ -x /usr/sbin/sysctl ]; then
	echo "#"					>> system-statistics
	echo "# sysctl kern hw net debug"		>> system-statistics
	echo "#"					>> system-statistics
	/usr/sbin/sysctl kern hw net debug		>> system-statistics	2>&1
fi

if [ -x /usr/bin/zprint ]; then
	echo "#"					>> system-statistics
	echo "# zprint"					>> system-statistics
	echo "#"					>> system-statistics
	${PRIV} /usr/bin/zprint				>> system-statistics	2>&1
fi

if [ -x /usr/sbin/lsof -a -x /bin/ls ]; then
	N=0
	/bin/ls -1	/Library/Preferences/SystemConfiguration/*-lock		\
			2>/dev/null						\
	| while read lock
	do
		if [ ${N} -eq 0 ]; then
			echo "#"					>> system-statistics
			echo "# lsof [SCPreferences lock files]"	>> system-statistics
		fi
		N=`expr ${N} + 1`

		echo "#"						>> system-statistics
		${PRIV} /usr/sbin/lsof +c 0 -- ${lock}			>> system-statistics	2>&1
	done
fi

#
# collect executable and plugin info
#
report_binary_info()
{
    if [ ! -f "${1}" ]; then
	return
    fi

    VERSION=`what "${1}"`
    echo "${VERSION}"					>> versions	2>&1

    SUM=`sum "${1}"`
    echo "\tsum: ${SUM}"				>> versions	2>&1

    LSINFO=`ls -lu "${1}"`
    echo "\tadditional info: ${LSINFO}"			>> versions	2>&1

    echo ""						>> versions	2>&1
}

get_binary_info()
{
    for BIN in										\
	/usr/libexec/bootpd								\
	/usr/libexec/configd								\
	/usr/libexec/discoveryd								\
	/usr/sbin/awacsd								\
	/usr/sbin/mDNSResponder								\
	/usr/sbin/pppd									\
	/usr/sbin/racoon								\
	/usr/libexec/misd								\
	/usr/libexec/InternetSharing							\
	/System/Library/Frameworks/SystemConfiguration.framework/SystemConfiguration	\

    do
	report_binary_info "${BIN}"
    done

    if [ -x /usr/bin/xcode-select -a -x /usr/bin/xcodebuild -a -x /usr/bin/xcrun ]; then
	SDKPATH="`xcode-select --print-path 2>/dev/null`"
	if [ $? -eq 0 -a -n "${SDKPATH}" ]; then
	    /usr/bin/xcodebuild -showsdks 2>/dev/null	\
	    | grep iphone				\
	    | awk '{ print $NF }'			\
	    | while read SDK
	    do
		SDKPATH="`xcrun --sdk $SDK --show-sdk-path`"
		for BIN in										\
		    /usr/libexec/configd_sim								\
		    /System/Library/Frameworks/SystemConfiguration.framework/SystemConfiguration	\

		do
		    report_binary_info "${SDKPATH}${BIN}"
		done
	    done
	else
	    echo "*** NO SDKs ***"	>> versions
	    echo ""			>> versions
	fi
    fi
}

get_plugins_info()
{
    num=0
    cd /System/Library/SystemConfiguration
    for PLUGIN in *.bundle
    do
	plugins[$num]="${PLUGIN}"
	num=$(( $num + 1 ))
    done

    cd "${WORKDIR}"

    for PLUGIN in "${plugins[@]}"
    do
	PLUGIN_DIR="/System/Library/SystemConfiguration/${PLUGIN}"
	PLUGIN_INF="${PLUGIN_DIR}/Contents/Info.plist"
	if [ ! -f "${PLUGIN_INF}" ]; then
	    PLUGIN_INF="${PLUGIN_DIR}/Info.plist"
	    if [ ! -f "${PLUGIN_INF}" ]; then
		echo "${PLUGIN_INF}: No Info.plist"		>> versions		2>&1
	    fi
	fi

	echo "${PLUGIN}"					>> versions		2>&1

	ENABLED="Enabled"
	BOOL=`scutil --get "${PLUGIN_INF}" / Enabled					2>/dev/null`
	if [ $? -eq 0 ]; then
	    if [ ${BOOL} = "TRUE" ]; then
		ENABLED="Enabled*"
	    else
		ENABLED="Disabled"
	    fi
	fi
	echo "\t${ENABLED}"					>> versions		2>&1

	VERBOSE=""
	BOOL=`scutil --get "${PLUGIN_INF}" / Verbose					2>/dev/null`
	if [ $? -eq 0 ]; then
	    if [ ${BOOL} = "TRUE" ]; then
		VERBOSE="Verbose"
	    fi
	fi
	if [ -n "${VERBOSE}" ]; then
		echo "\t${VERBOSE}"				>> versions		2>&1
	fi

	VERSION=`scutil --get "${PLUGIN_INF}" / CFBundleVersion				2>/dev/null`
	if [ $? -eq 1 ]; then
		VERSION=`scutil --get "${PLUGIN_INF}" / CFBundleShortVersionString	2>/dev/null`
	fi
	echo "\tVersion: ${VERSION}"				>> versions		2>&1

	if [ -f "${PLUGIN_DIR}/Contents/MacOS/${PLUGIN%.*}" ]; then
	    SUM=`sum "${PLUGIN_DIR}/Contents/MacOS/${PLUGIN%.*}"`
	    echo "\tsum: ${SUM}"				>> versions		2>&1

	    LSINFO=`ls -lu "${PLUGIN_DIR}/Contents/MacOS/${PLUGIN%.*}"`
	    echo "\tadditional info: ${LSINFO}"			>> versions		2>&1
	elif [ -f "${PLUGIN_DIR}/${PLUGIN%.*}" ]; then
	    SUM=`sum "${PLUGIN_DIR}/${PLUGIN%.*}"`
	    echo "\tsum: ${SUM}"				>> versions		2>&1

	    LSINFO=`ls -lu "${PLUGIN_DIR}/${PLUGIN%.*}"`
	    echo "\tadditional info: ${LSINFO}"			>> versions		2>&1
	fi

	echo ""							>> versions		2>&1
    done
}

if [ -x /usr/bin/what -a -x /usr/bin/sum -a -x /bin/ls ]; then
	get_binary_info
	get_plugins_info
fi

#
# collect the logarchive
#
if [ -x /usr/bin/log ]; then
	LOGARCHIVE_START_TIME=`date -v -1d +"%Y-%m-%d %H:%M:%S"`
	LOGARCHIVE_OUTPUT="system_logs.logarchive"
	${PRIV} /usr/bin/log collect --livedata --output "${LOGARCHIVE_OUTPUT}" --start "${LOGARCHIVE_START_TIME}"	2>/dev/null
	if [ -d ${LOGARCHIVE_OUTPUT} ]; then
		${PRIV} chown -R ${UID} "${LOGARCHIVE_OUTPUT}"
	fi
fi

#
# dmesg
#
if [ -x /sbin/dmesg ]; then
	${PRIV} /sbin/dmesg							> dmesg
fi

#
# ppp log file(s)
#
scutil <<_END_OF_INPUT				\
| awk -F' *: *'					\
    '						\
     /Logfile : / {				\
       if (index($2, "/") == 1) { print $2 }	\
       else { print "/var/log/ppp/" $2 }	\
     }						\
     END {					\
       print "/tmp/pppotcp.log"			\
     }						\
    '						\
| sort -u					\
| while read logFile
open
show Setup:/Network/Service/[^/]+/PPP pattern
quit
_END_OF_INPUT
do
	if [ -f "${logFile}" ]; then
		b="`basename ${logFile}`"
		cat "${logFile}"			> "${b}"		2>&1
	fi
done

if [ -x /bin/ls ]; then
	#
	# collect crash reports
	#
	for daemon in				\
			InternetSharing		\
			SCHelper		\
			SCMonitor		\
			awacsd			\
			bootpd			\
			configd			\
			discoveryd		\
			discoveryd_helper	\
			eapolclient		\
			mDNSResponder		\
			mDNSResponderHelper	\
			pppd			\
			racoon			\
			socketfilterfw		\

	do
		/bin/ls -1	/Library/Logs/DiagnosticReports/${daemon}_*.crash	\
				/Library/Logs/DiagnosticReports/${daemon}_*.ips		\
				/Library/Logs/CrashReporter/${daemon}_*.crash		\
				/Library/Logs/CrashReporter/${daemon}_*.ips		\
				/Library/Logs/CrashReporter/${daemon}_*.plist		\
				2>/dev/null						\
		| while read log
		do
			if [ -f "${log}" ]; then
				b="`basename ${log}`"
				${PRIV} cat "${log}"		> "${b}"		2>&1
			fi
		done
	done
fi

#
# stackshot
#
if [ -x /usr/local/bin/crstackshot ]; then
	/usr/local/bin/crstackshot				2>/dev/null
fi

#
# wait for background activity (eg: lsof)
#
wait

#
# Stop the packet capture
#
stop_pcap

if [ $NO_TAR -eq 0 ]; then
	#
	# collect everything into a single archive
	#
	cd "${WORKDIR}/.."
	tar -c ${GZ_OPT} -f "${ARCHIVE}" "${OUT}"
	rm -rf "${WORKDIR}"

	if [ ${UID} -eq 0 ]; then
		if [ -n "${SUDO_UID}" -a -n "${SUDO_GID}" ]; then
			if [ ${UID} -ne ${SUDO_UID} ]; then
				chown ${SUDO_UID}:${SUDO_GID} "${ARCHIVE}"
			fi
		fi
	fi

	echo "Network data collected to \"${ARCHIVE}\""
else
	mv "${WORKDIR}" "${OUTDIR}"

	if [ ${UID} -eq 0 ]; then
		if [ -n "${SUDO_UID}" -a -n "${SUDO_GID}" ]; then
			if [ ${UID} -ne ${SUDO_UID} ]; then
				chown -R ${SUDO_UID}:${SUDO_GID} "${OUTDIR}/${OUT}"
			fi
		fi
	fi
	echo "Network data collected to \"${OUTDIR}/${OUT}\""
fi
